#include "http_object.h"

#include <string>
#include <cstring>

#include "common.h"
#include "curl.h"

namespace NodeApi {
napi_ref HttpObject::g_httpRef = nullptr;
HttpObject::HttpObject() {}

HttpObject::~HttpObject() {}

napi_value HttpObject::Request(napi_env env, napi_callback_info info)
{
    napi_value result = nullptr;
    std::string on = "request";
    NAPI_CALL(env, napi_create_string_utf8(env, on.c_str(), on.size(), &result));
    return result;
}

napi_value HttpObject::On(napi_env env, napi_callback_info info)
{
    napi_value result = nullptr;
    const char *curlVersion = curl_version();
    std::string on = "on";
    NAPI_CALL(env, napi_create_string_utf8(env, curlVersion, strlen(curlVersion), &result));
    return result;
}

napi_value HttpObject::Off(napi_env env, napi_callback_info info)
{
    napi_value result = nullptr;
    std::string on = "off";
    NAPI_CALL(env, napi_create_string_utf8(env, on.c_str(), on.size(), &result));
    return result;
}

napi_value HttpObject::Destroy(napi_env env, napi_callback_info info)
{
    napi_value result = nullptr;
    std::string on = "destroy";
    NAPI_CALL(env, napi_create_string_utf8(env, on.c_str(), on.size(), &result));
    return result;
}

napi_value HttpObject::HttpConstructor(napi_env env, napi_callback_info info)
{
    HttpObject *httpObject = new (std::nothrow) HttpObject();
    if (httpObject == nullptr) {
        napi_value result = nullptr;
        napi_get_null(env, &result);
        return result;
    }
    napi_value thisVar = nullptr;
    napi_wrap(
        env, thisVar, httpObject,
        [](napi_env env, void *data, void *hint) {
            // napi_finalize release the sources
            HttpObject *httpObject = (HttpObject *)data;
            if (httpObject) {
                delete httpObject;
                httpObject = nullptr;
            }
        },
        nullptr, nullptr);
    return thisVar;
}

napi_value HttpObject::InitHttp(napi_env env, napi_value exports)
{
    napi_property_descriptor properties[] = {
        DECLARE_NAPI_FUNCTION("request", Request),
        DECLARE_NAPI_FUNCTION("on", On),
        DECLARE_NAPI_FUNCTION("off", Off),
        DECLARE_NAPI_FUNCTION("destory", Destroy),
    };
    NAPI_CALL(env, napi_define_properties(env, exports, sizeof(properties) / sizeof(*properties), properties));
    napi_value constructor = nullptr;
    NAPI_CALL(env,
        napi_define_class(env, "Http", NAPI_AUTO_LENGTH, HttpConstructor, nullptr,
            sizeof(properties) / sizeof(*properties), properties, &constructor));

    napi_create_reference(env, constructor, 1, &g_httpRef);
    napi_set_named_property(env, exports, "Http", constructor);
    return exports;
}

// /////////////////////////////////////////////////////////////////////////////////////////////////
// struct AddContext : BaseContext {
//     int32_t num1 = 0;
//     int32_t num2 = 0;
//     int32_t addResult = 0;
// };
// static void NativeAdd(napi_env env, void *data)
// {
//     auto addContext = static_cast<AddContext *>(data);
//     addContext->addResult = addContext->num1 + addContext->num2;
// }

// static void AddCallback(napi_env env, napi_status status, void *data)
// {
//     auto addContext = static_cast<AddContext *>(data);
//     napi_value resultValue = nullptr;
//     napi_create_int32(env, addContext->addResult, &resultValue);
//     napi_value callbackValues[] = {resultValue};
//     napi_value callback;
//     NAPI_CALL_RETURN_VOID(env, napi_get_reference_value(env, addContext->callbackRef, &callback));
//     napi_value global;
//     NAPI_CALL_RETURN_VOID(env, napi_get_global(env, &global));

//     napi_value result;
//     NAPI_CALL_RETURN_VOID(env, napi_call_function(env, global, callback, 1, callbackValues, &result));

//     NAPI_CALL_RETURN_VOID(env, napi_delete_reference(env, addContext->callbackRef));
//     NAPI_CALL_RETURN_VOID(env, napi_delete_async_work(env, addContext->work));
// }

// static napi_value AddAsync(napi_env env, napi_callback_info info)
// {
//     std::cout << "AddAsync start" << std::endl;
//     size_t parameterCount = 3;
//     napi_value parameters[3] = {0};
//     napi_value thisVar = nullptr;
//     void *data = nullptr;
//     NAPI_CALL(env, napi_get_cb_info(env, info, &parameterCount, parameters, &thisVar, &data));
//     NAPI_ASSERT(env, MatchAddAsyncParameter(env, parameterCount, parameters), "parameters missmatch");
//     auto context = new AddContext();
//     napi_get_value_int32(env, parameters[0], &context->num1);
//     napi_get_value_int32(env, parameters[1], &context->num2);
//     napi_create_reference(env, parameters[2], 1, &context->callbackRef);
//     std::cout << "AddAsync num1 = " << context->num1 << std::endl;
//     std::cout << "AddAsync num2 = " << context->num2 << std::endl;
//     napi_value resourceName = nullptr;
//     std::string workNameStr = "addAsync";
//     NAPI_CALL(env, napi_create_string_utf8(env, workNameStr.data(), NAPI_AUTO_LENGTH, &resourceName));
//     NAPI_CALL(
//         env, napi_create_async_work(env, nullptr, resourceName, NativeAdd, AddCallback, context, &context->work));
//     NAPI_CALL(env, napi_queue_async_work(env, context->work));
//     return NapiUtil::CreateUndefined(env);
// }

// static void NativeCurl(napi_env env, void *data)
// {
//     std::cout << "nativeCurl start" << std::endl;
//     CURL *curl;
//     CURLcode res;

//     curl = curl_easy_init();
//     if (curl) {
//         curl_easy_setopt(curl, CURLOPT_URL, "https://example.com");

//         /* cache the alternatives in this file */
//         curl_easy_setopt(curl, CURLOPT_ALTSVC, "altsvc.txt");

//         /* restrict which HTTP versions to use alternatives */
//         curl_easy_setopt(curl, CURLOPT_ALTSVC_CTRL, (long)CURLALTSVC_H1 | CURLALTSVC_H2 | CURLALTSVC_H3);

//         /* Perform the request, res will get the return code */
//         res = curl_easy_perform(curl);
//         /* Check for errors */
//         if (res != CURLE_OK)
//             fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));

//         /* always cleanup */
//         curl_easy_cleanup(curl);
//     }
// }

// static void CurlCallback(napi_env env, napi_status status, void *data)
// {
//     std::cout << "curlCallback start" << std::endl;
//     auto baseContext = static_cast<BaseContext *>(data);
//     if (baseContext->callbackRef) {
//         napi_delete_reference(env, baseContext->callbackRef);
//     }
//     delete baseContext;
// }

// static napi_value CurlTest(napi_env env, napi_callback_info info)
// {
//     // size_t parameterCount = 2;
//     // napi_value parameters[2] = {0};
//     // napi_value thisVar = nullptr;
//     // void *data = nullptr;
//     // NAPI_CALL(env, napi_get_cb_info(env, info, &parameterCount, parameters, &thisVar, &data));
//     std::cout << "curlTest start" << std::endl;
//     napi_value resourceName = nullptr;
//     std::string workNameStr = "curl_test";
//     napi_create_string_utf8(env, workNameStr.data(), NAPI_AUTO_LENGTH, &resourceName);
//     auto context = new BaseContext();
//     napi_status createWorkStatus =
//         napi_create_async_work(env, nullptr, resourceName, NativeCurl, CurlCallback, (void *)context,
//         &context->work);
//     if (createWorkStatus == napi_ok) {
//         napi_queue_async_work(env, context->work);
//     }
//     return NapiUtil::CreateUndefined(env);
// }

} // namespace NodeApi